#ifndef __LOOPER__
#define __LOOPER__
#include <thread>
#include <functional>
#include <condition_variable>
#include "Buffer.hpp"
extern std::ofstream ofs;
namespace Log
{

    // 这里加一个设计：异步日志器中
    // 将操作双缓冲区的，单独在拿出来进行一个封装
    // 它包含了生产者,也包含消费者


    using callback = std::function<void(Buffer&)>;
    class AsyncLooper
    {
        // 消费者的入口函数：即如何处理日志消息,具体的落地方案
    private:
        // 不适用父类中的锁，重新整一套
        std::mutex _mt_cp; // 消费者与生产者的锁
        std::mutex _mt_pro;  // 生产者与生产者之间的锁
        std::condition_variable _cond_con; //消费者条件变量
        std::condition_variable _cond_pro; //生产条件变量
        // 生产者是当前执行流


        Buffer _buf_consumer; // 消费缓冲区
        Buffer _buf_producer; // 生产缓冲区
        AsyncType _type; // 异步日志器的类型 是安全模式还是非安全模式
        // 使用原子类型,保证线程的安全
        // 懒得使用锁了
        std::atomic<bool> _running; // 判断日志器是否运行
        callback _sink; // 消费者线程对日志信息的处理方式--具体的落地方式
        std::thread _consumer; // 消费者
    public:
        using ptr = std::shared_ptr<AsyncLooper>;
        AsyncLooper(const callback sink,AsyncType type = AsyncType::ASYNC_UNSAFE)
        :_sink(sink),
        _type(type),
        _buf_consumer(type),
        _buf_producer(type),
        _running(true),
        _consumer(&AsyncLooper::consume,this) // 绑定本类中的成员,需要 (&类名::成员函数,this)
        // _consumer(std::thread(&AsyncLooper::consume,this)) // 使用匿名对象进行构造
        {}
        // 没写析构函数,线程忘记释放了！
        // 然后一直报错
        ~AsyncLooper()
        {
            stop();
        }
        // 消费线程
        void consume()
        {
            // std::cout << "异步线程启动" << std::endl;
            while(true)
            {

                // error
                // 这样写是不对的,不能先交换。不是判断是否存在可读的空间
                // 刚开始buffer都会空,如果消费者先执行,那么消费者一直会被阻塞了唤不醒了。

                // 我们应该去判断 生产缓冲区中是否存在数据,存在数据则交换,再读取！
                // // 1.加锁保护 生产者与消费者之间的锁
                // std::unique_lock<std::mutex> ulock(_mt_cp);
                // // 2.将生产缓冲区内部的数据读取到消费缓冲区
                // _buf_consumer.swap(_buf_producer);
                // // 3.获取数据之前,要保证有足够的数据
                // _cond_con.wait(ulock,[&]()
                // {
                //     // 为假则表示条件不满足,继续等待
                //     // 为真则表示条件满足,线程唤醒
                //     return _buf_consumer.readable() != 0 || _running == false;
                // });

                // {} 内部是临界区
                {
                    // 正确写法
                    // 1.加锁保护 生产者与消费者之间的锁
                    std::unique_lock<std::mutex> ulock(_mt_cp);

                    // 2. 判断生产缓冲区中是否有数据,无则阻塞等待,有则唤醒线程

                    // 循环写法 防止误唤醒
                    // while(_buf_producer.empty() == true && _running != false)
                    //       _cond_con.wait(ulock);
                    // 另一种写法
                    _cond_con.wait(ulock,[&]()
                    {
                        // 为假则表示条件不满足,继续等待
                        // 为真则表示条件满足,线程唤醒
                        
                        return !_buf_producer.empty() || _running == false;
                    });
                    
                    // 0.处理异步日志器被关闭的情况
                    // 两个条件同时满足才能退出消费者线程
                    // 缓冲区内部有数据需要读取完、
                    // 只有缓冲区内部无数据且运行标志位为false 才能结束线程。

                    // 服了啊！！ 是生产缓冲区中还有数据，则不能退出！！
                    // if(_running == false && _buf_consumer.empty())

                    if(_running == false && _buf_producer.empty())
                        break;
                        
                    // 3.将生产缓冲区内部的数据读取到消费缓冲区
                    // _buf_consumer.swap(_buf_producer);
                    _buf_producer.swap(_buf_consumer);
                }
                // 4. 唤醒生产者
                // 安全模式下,才需要唤醒生产者
                // 非安全模式下,不需要唤醒
                if(_type == AsyncType::ASYNC_SAFE)
                {
                    _cond_pro.notify_all();
                    // std:: cout << "我是消费者,我唤醒生产者" << std::endl;
                }
                // 5.日志消息进行落地
                _sink(_buf_consumer);
                // 6.缓冲区数据已经被拿走了,需要重置下标
                _buf_consumer.reset();

            
            }
        }


        // 生产线程
        // 将日志信息 写入生产缓冲区中
        void product(const string& msg)
        {   
            // 消费者-异步日志器-新线程
            // 生产者-异步日志器-主线程
            // 异步日志器只有一个,即生产缓冲区,消费缓冲区只有一个
            // 多生产者,单消费者模式下：

            // 1.加锁保证线程安全
            // std::unique_lock<std::mutex> ulock_p(_mt_pro);  // 生产者之间的锁
            std::unique_lock<std::mutex> ulock_cp(_mt_cp);  // 生产者与消费者之间的锁

            // 2.写入之前要保证有足够的空间进行写入
            //   只有缓冲区内部有空间才进行写入
            //   缓冲区内部没有空间写入则阻塞
            //   不能释放ulock_p,当前生产者把ulock_p释放了,别的消费者来时，同样也会因为相同的条件而被阻塞
            //   需要释放ulock_cp,让消费者尽快读取完数据,然后交换缓冲区,才能把生产者唤醒

            //  这样写是不对的,需要用循环,并且还少写了一个条件 _running != false时也需要被唤醒
            //  还有需要把 异步日志的安全模式考虑上! 

            //  if(_buf_producer.writeable() <= msg.size())
            //      _cond_pro.wait(ulock_cp);
            //  循环写法：条件不满足时,阻塞  防止误唤醒
            //  if(_type == AsyncType::ASYNC_SAFE)
            //      while(_buf_producer.writeable() < msg.size() && _running != false)
            //          _cond_pro.wait(ulock_cp);

            //  wait(lck,pred)   等价于   while(!pred()) wait(lck)
            //  另一种写法
            if(_type == AsyncType::ASYNC_SAFE)
            {
                _cond_pro.wait(ulock_cp,[&]()
                {
                    // 为假则表示条件不满足,继续等待
                    // 为真则表示条件满足,线程唤醒
                    return _buf_producer.writeable() >= msg.size();
                    // std::cout << "我是生产者,我在等待" << std::endl;

                });
            }
            // 生产者是不需要判断_running == false的,它不是循环

            // 3. 写入
            _buf_producer.push(msg.c_str(),msg.size());
            // 4. 唤醒消费者线程
            // std::cout << "我是生产者,我唤醒了消费者" << std::endl;
            _cond_con.notify_all();

        }

        // 停止日志器
        void stop()
        {
            _running = false;
            // 唤醒所有消费者线程
            _cond_con.notify_all();
            _consumer.join();
        }

      
    };
}

#endif